#include "ConsoleWidget.h"
#include <SEUi/SEUi.h>
#include <SEDebug/Log/Log.h>
#include <deque>

namespace SEngine
{

    struct LogEntry {
        std::string message;
        ImVec4 color;
        std::string time;
        Log::Level lvl;
    };
    constexpr int MAX_LOG_COUNT = 5000;
    struct LogWindow {
        std::deque<LogEntry> entries;
        bool autoScroll = true;
        bool scrollToBottom = false;
        ImGuiTextFilter filter;
        
        void AddLog(const std::string& message, const Log::Level& lvl, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
        {
            auto now = std::chrono::system_clock::now();
            auto time = std::chrono::system_clock::to_time_t(now);
            char timeBuf[64];
            std::strftime(timeBuf, sizeof(timeBuf), "%H:%M:%S", std::localtime(&time));
            entries.push_back({message, color, timeBuf, lvl});
            scrollToBottom = autoScroll;
            if (entries.size() == MAX_LOG_COUNT) {
                entries.pop_front();
            }
        }
    };

    void DrawLogWindow(LogWindow& logWindow)
    {
        if (ImGui::BeginPopup("Options"))
        {
            ImGui::Checkbox("Auto-scroll", &logWindow.autoScroll);
            ImGui::EndPopup();
        }

        if (ImGui::Button("Options"))
            ImGui::OpenPopup("Options");
        ImGui::SameLine();
        bool clear = ImGui::Button("Clear");
        ImGui::SameLine();
        logWindow.filter.Draw("Filter", -100.0f);

        ImGui::Separator();

        ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);

        if (clear)
            logWindow.entries.clear();

        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 1));
        
        for (const auto& entry : logWindow.entries)
        {
            std::unordered_map<Log::Level, std::pair<std::string, ImVec4>> table = {
                {Log::Level::debug, {"[debug] ", ImVec4(0.5f, 0.5f, 0.5f, 1.0f)}},
                {Log::Level::err, {"[error] ", ImVec4(1.0f, 0.4f, 0.4f, 1.0f)}},
                {Log::Level::info, {"[info] ", ImVec4(0.2f, 0.847f, 0.2f, 1.0f)}},
                {Log::Level::warn, {"[warn] ", ImVec4(1.0f, 0.843f, 0.0f, 1.0f)}},
            };
            if (!logWindow.filter.PassFilter((table.at(entry.lvl).first + entry.message).c_str()))
                continue;
            ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "[%s] ", entry.time.c_str());
            ImGui::SameLine(0, 0);
            ImGui::TextColored(table.at(entry.lvl).second, table.at(entry.lvl).first.c_str());
            ImGui::SameLine(0, 0);
            ImGui::TextColored(entry.color, "%s", entry.message.c_str());
        }

        ImGui::PopStyleVar();

        if (logWindow.scrollToBottom && logWindow.autoScroll)
            ImGui::SetScrollHereY(1.0f);
        logWindow.scrollToBottom = false;

        ImGui::EndChild();
    }

    ConsoleWidget::ConsoleWidget(EditLayer & layer)
        : m_layer(layer)
    {
    }

    ConsoleWidget::~ConsoleWidget()
    {
    }

    void ConsoleWidget::UpdateImpl(int delta)
    {
        DrawLogWindow(GetLog());
    }

    LogWindow &ConsoleWidget::GetLog()
    {
        static LogWindow entry;
        return entry;
    }

    int ConsoleWidgetInit = ([](){
        Log::GetInstance().SignalAddLog.AddSlot([](const std::string & str, Log::Level lvl) {
            ConsoleWidget::GetLog().AddLog(str, lvl);
            return SlotState::Keep;
        });
    }(), 0);

} // namespace SEngine
